﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using Weazel.Willows.Model;

namespace Weazel.Willows.PreliminaryModel
{
	public class Model 
	{
		private TypeRegistry typeRegistry;
		public TypeRegistry TypeRegistry
		{
			get { return typeRegistry; }
		}

		/// <summary>
		/// List of modules defined in the design file indexed
		/// on their local names.
		/// </summary>
		private Dictionary<string, Module> topLevelModules;
		public Dictionary<string, Module> TopLevelModules
		{
			get { return topLevelModules; }
		}

		public Model(ref List<ModelError> errors, XmlElement xmlElement)
		{
			this.topLevelModules = new Dictionary<string, Module>();

			// create the type registry (before reading xml)
			this.typeRegistry = new TypeRegistry();

			// read the model from the xml description
			read(ref errors, xmlElement);
		}

		public Weazel.Willows.Model.Model Expand(List<ModelError> errors, string topModuleName)
		{
			Weazel.Willows.Model.Model model = new Weazel.Willows.Model.Model();

			if (!topLevelModules.ContainsKey(topModuleName))
			{
				string message =
				  string.Format("Cannot find top level module '{0}'", topLevelModules);

				errors.Add(new ModelError(message));

				// cannot continue
				return null;
			}

			Module topTemplate = topLevelModules[topModuleName];

			model.Top = 
				topTemplate.Expand(this, new HierarchicalEntity.HierarchyRoot(), new HierarchicalName(), topModuleName);

			return model;
		}

		private void read(ref List<ModelError> errors, XmlElement description)
		{
			System.Diagnostics.Debug.Assert(description.Name == "Model");

			readTypes(ref errors, description);
			readTopLevelModules(ref errors, description);
			readSystems(ref errors, description);
		}

		private void readTypes(ref List<ModelError> errors, XmlElement description)
		{
			XmlNodeList types = description.GetElementsByTagName("TypeDef");
			this.typeRegistry.Read(ref errors, types);
		}

		private void readSystems(ref List<ModelError> errors, XmlElement description)
		{
			foreach (XmlElement element in description.GetElementsByTagName("System"))
			{
				system system = new system(this, ref errors, element);
				insertModule(ref errors, system);
			}
		}

		private void readTopLevelModules(ref List<ModelError> errors, XmlElement description)
		{
			foreach (XmlElement element in description.GetElementsByTagName("Module"))
			{
				Module module = new Module(this, ref errors, element);
				insertModule(ref errors, module);
			}
		}

		private void insertModule(ref List<ModelError> errors, Module module)
		{
			// check for duplicates
			if (topLevelModules.ContainsKey(module.Name))
			{
				string message =
					string.Format("Duplicate definition of module '{0}'", module.Name);
				errors.Add(new ModelError(message));
			}
			else
			{
				// add to list of top level modules
				topLevelModules.Add(module.Name, module);
			}
		}
	}
}
